package com.zz.ecommerce.common;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;

import java.lang.reflect.*;
import java.util.Properties;

/**
 * Created by Administrator on 2015-12-15.
 */
@Intercepts({@org.apache.ibatis.plugin.Signature(type=Executor.class, method="query", args={MappedStatement.class, Object.class, org.apache.ibatis.session.RowBounds.class, org.apache.ibatis.session.ResultHandler.class}),
        @org.apache.ibatis.plugin.Signature(type=Executor.class, method="update", args={MappedStatement.class, Object.class})})
public class MapperInterceptor implements Interceptor
{
    private static final Class GENERIC_CLASS = CommonDao.class;

    public Object intercept(Invocation invocation) throws Throwable
    {
        genericReturnType(invocation.getArgs()[0]);

        return invocation.proceed();
    }

    private void genericReturnType(Object o) throws NoSuchFieldException, IllegalAccessException {
        MappedStatement ms = (MappedStatement) o;

        String msId = ms.getId();
        Class mapperClass = getMapperClass(msId);

        Type[] types = mapperClass.getGenericInterfaces();
        if(null == types || types.length == 0)
        {
            return;
        }

        for (Type type : types) {
            if (!(type instanceof ParameterizedType)) {
                continue;
            }

            ParameterizedType paramType = (ParameterizedType) type;
            if(!paramType.getRawType().equals(GENERIC_CLASS))
            {
                continue;
            }

            String methodName = getMethodName(msId);
            Method[] methods = GENERIC_CLASS.getMethods();
            for(Method m : methods)
            {
                if(!methodName.equals(m.getName()))
                {
                    continue;
                }

                Type returnType = m.getGenericReturnType();

                if(returnType instanceof TypeVariable)
                {
                    if("T".equals(((TypeVariable) returnType).getName()))
                    {
                        ResultMap resultMap = ms.getResultMaps().get(0);

                        Field f = ResultMap.class.getDeclaredField("type");
                        f.setAccessible(true);
                        f.set(resultMap, paramType.getActualTypeArguments()[0]);
                    }

                    continue;
                }
                if(returnType instanceof ParameterizedType)
                {
                    if("T".equals(((ParameterizedType) returnType).getActualTypeArguments()[0].toString()))
                    {
                        ResultMap resultMap = ms.getResultMaps().get(0);

                        Field f = ResultMap.class.getDeclaredField("type");
                        f.setAccessible(true);
                        f.set(resultMap, paramType.getActualTypeArguments()[0]);
                    }

                    continue;
                }
            }
        }
    }

    public Class<?> getMapperClass(String msId)
    {
        String mapperClassStr = msId.substring(0, msId.lastIndexOf("."));
        try {
            return Class.forName(mapperClassStr); } catch (ClassNotFoundException e) {
        }
        throw new RuntimeException(new StringBuilder().append("无法获取Mapper接口信息:").append(msId).toString());
    }

    public String getMethodName(String msId)
    {
        return msId.substring(msId.lastIndexOf(".") + 1);
    }

    public Object plugin(Object target)
    {
        if ((target instanceof Executor)) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    public void setProperties(Properties properties)
    {
    }
}
